iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 24
3
Modern Web

從比入門再往前一點開始,一直到深入React.js系列 第 24

【Day.24】React效能 - 用lazy和Suspense來動態載入元件

  • 分享至 

  • xImage
  •  

(2024/04/06更新) 因應React在18後更新了許多不同的語法,更新後的教學之後將陸續放在 新的blog 中,歡迎讀者到該處閱讀,我依然會回覆這邊的提問


在前面我們說,過去使用原生JS編寫有規模的專案時,因為要模組化,檔案越分越多,最後零散在各處。為了解決這個問題,後來工程師使用打包工具,把所有的JS檔綁成一個bundle.js,在第一次執行網頁時就載入所有程式碼

但是這也造成了一個問題。

「當專案規模過大時,bundle.js會很大,導致第一次載入網頁的時間太久。」

這個時候我們就會希望把那些「使用者不會很常進去的頁面」從bundle.js拿出來。但是要怎麼做呢? 難道還要去webpack.config.js慢慢設定嗎?

Code-Splitting with React.lazy

在先前,我們要引入component檔案時,是使用:

import InputForm from '../component/InputForm';

而React提供了一個特殊的引入元件方法lazy()。Babel會把用lazy()引入的元件在打包時拆成一個獨立的js檔案,並且只有在第一次要渲染該元件的時候,才會引入該js檔。它的用法是

const 元件 = React.lazy(() => import('檔案相對路徑'));

但是元件載入要一段時間,我們要怎麼處理lazy元件還沒被載入的狀況呢?

Suspense

Suspense是React提供的特殊元件,語法如下:

<Suspense fallback={讀取元件}>目標載入元件</Suspense>;

當「目標載入元件」還沒載入完成時,React會顯示fallback這個props綁定的「讀取元件」,一直到「目標載入元件」載入完成後再切換過來。

嗯?這樣我們是不是可以把它拿來處理ajax的狀況呢?

的確,React希望在未來的某一天全面讓大家捨棄在useEffect呼叫http request,並且全面改成使用Suspense。但目前相關的API還在實驗開發階段,詳請可以參考並關注官方文件

加入lazy和Suspense到我們的程式碼中

我們來試著在src/page/FormPage.js使用lazy引入<InputForm />,並觀察程式碼的狀態:

1. 請先引入lazy和Suspense

import React, {lazy, Suspense} from 'react';

2. 用lazy引入InputForm

const InputForm = lazy(() => import('../component/InputForm'));

3. 用Suspense使用lazy InputForm元件

import React, {lazy, Suspense} from 'react';

const InputForm = lazy(() => import('../component/InputForm'));

const FormPage = () =>{
    return <Suspense fallback={<div>Loading...</div>}>
                <InputForm />
            </Suspense>;
}

export default FormPage;

4. 執行npm run build

此時你會發現,build資料夾出現了一個新的檔案

我們去搜尋本來的bundle.js,會發現裡面沒有InputForm裡面的「提交」按鈕了

而搜尋新的1.bundle.js,會發現InputForm裡面的「提交」按鈕在它裡面。

最後我們查看執行結果,會發現在第一次切換到FormPage的時候,React去get了這一隻新的1.bundle.js

這樣我們就實現了動態延遲載入元件。

需要注意的是,lazy和Suspense目前還不支援Server-side-render,如果你想在相關頁面做SSR,就必須要使用Loadable Components


上一篇
【Day.23】React效能 - 用key避免陣列元件的重複渲染
下一篇
【Day.25】React進階 - Custom hook | 把React component API模組化吧!
系列文
從比入門再往前一點開始,一直到深入React.js30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言